<?php
	
	namespace Aspic;
	
	use Aspic as A;
	use Aspic\File\File;
	
	/**
	* @deprecated Use Aspic\FileSession or Aspic\DbSession instead
	*/
	class Session {
		
		use OptionTrait {
			__construct as optionTraitInit;
		}
		
		const SESSIONS_DATA_FILE_NAME = 'sessions.data';
	
		protected $_storageDir;
		protected $_lifeTime;
		protected $_cookieName;
		protected $_httpOnly;
		protected $_password;
		
		protected $_sessId;
		protected $_creationTime;
		protected $_navigatorCheck;
		protected $_langsCheck;
		protected $_ipCheck;
		protected $_cleanInterval;
		
		protected $_sessionVars;
		protected $_hasBeenModified = false;
		
		public function __construct($storageDir = '.', $lifeTime = 86400, array $options = array()) {
			$this->optionTraitInit();
			
			$this->sessionVars = array();
			$this->hasBeenModified = false;
			
			// because in destruct the CU (current directory is not the same as in others methods see php doc)
			$this->storageDir = realpath($storageDir);
			$this->lifeTime = $lifeTime;
			
			$this->cookieName = 'PHPSESSID';
			$this->httpOnly = true;
			$this->password = ''; // salt for generation of sessions ids
			$this->navigatorCheck = true;
			$this->langsCheck = false;
			$this->ipCheck = false;
			$this->cleanInterval = 86400;
			
			$this->checkClean();
			// var_dump($this);
			$this->loadSession(Util::getIfSet($_COOKIE[$this->cookieName]));
		}
		
		public function __destruct() {
			if($this->hasBeenModified) {
				$data = $this->getCurrentSessionData();
				
				$this->saveSession($this->sessId, $data);
			}
		}
	
		public function &get($var) {
			$var = &$this->sessionVars[$var];
			
			if(is_object($var)) {
				// Maybe object properties will be updated
				$this->hasBeenModified = true;
			}
			
			return $var;
		}
		
		protected function setVars(array $vars) {
			$this->hasBeenModified = true;
			
			$this->sessionVars = $vars;
		}
		
		public function set($varName,$value) {
			$this->sessionVars[$varName] = $value;
			
			$this->hasBeenModified = true;
		}
		
		protected function getSessionPath($id) {
			return $this->storageDir.'/'.str_replace('..', '', $id).'.session';
		}
		
		protected function loadSession($sessId = null) {
			if(isset($sessId) AND is_file($this->getSessionPath($sessId))) {
				$this->sessId = $sessId;
				
				$sessData = $this->getSessionData($this->sessId);
				$validSession = $this->isSessionDataValid($this->sessId, $sessData['creationTime'], $sessData['ip'], $sessData['navigator'], $sessData['acceptLanguages'], $sessData['data']);
//			var_dump($sessData['data']);
				if($validSession === false) {
					$this->removeSession($this->sessId);
					$this->newSession();
				}
				else {
					$this->creationTime = $sessData['creationTime'];
					$this->_vars = $sessData['data'];
				}
			}
			else {
				$this->newSession();
			}
		}
		
		protected function generateId() {
			do {
				$id = Security\Security::getRandomId($this->password);
			}
			while(is_file($this->getSessionPath($id)));
			
			return $id;
		}
		
		/**
		* Create a new session (file) as the current session
		*/
		protected function newSession($sessData = array(), $keepCreationTime = false) {
			$id = $this->generateId();
			
			if($keepCreationTime) {
				$time = $this->creationTime;
			}
			else {
				$time = time();
			}
			
			$data['creationTime'] = $time;
			$data['ip'] = $_SERVER['REMOTE_ADDR'];
			$data['navigator'] = $_SERVER["HTTP_USER_AGENT"];
			$data['acceptLanguages'] = A\Util::getIfset($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
			$data['data'] = $sessData;
			
			$this->saveSession($id, $data);
			
			$cookieExpire = $time+$this->lifeTime;
			
			setcookie($this->cookieName, $id, $cookieExpire, '/', '', false, true);
			
			$this->sessId = $id;
			$this->_vars = $sessData;
			$this->creationTime = $time;
			
			return $id;
		}
		
		/**
		* Remove session (file) that have expire
		*/
		protected function clean() {
			$sessionsFiles = $this->getAllSessions();
			
			foreach($sessionsFiles as $sessionFilePath) {
				$id = substr($sessionFilePath, strrpos($sessionFilePath,'/') + 1);
				$id = substr($id, 0, strpos($id, '.session'));
				
				if($this->hasSessionExpire($id)) {
					$this->removeSession($id);
				}
			}
		}
		
		/**
		* Return current session data as array
		*/
		protected function getCurrentSessionData() {
			return array(
				'creationTime' => $this->creationTime, 
				'ip' => $_SERVER['REMOTE_ADDR'], 
				'navigator' => $_SERVER["HTTP_USER_AGENT"], 
				'acceptLanguages' => $_SERVER["HTTP_ACCEPT_LANGUAGE"], 
				'data' => $this->_vars, 
			);
		}
		
		/**
		* Check if a session has expire (from session creation time)
		*/
		protected function hasSessionTimeExpire($sessionCreationTime) {
			return !(($sessionCreationTime + $this->lifeTime) > time());
		}
		
		/**
		* Check if a session has expire (from session id)
		*/
		protected function hasSessionExpire($sessId) {
			$sessData = $this->getSessionData($sessId);
			
			return $this->hasSessionTimeExpire($sessData['creationTime']);
		}
		
		protected function isSessionDataValid($id, $creationTime, $ip, $navigator, $langs) { // false if invalid and remove file if it exists, sessionVars if valid
			if(!is_string($id)) {
				throw new Exception('Session id is not valid ('.$id.')');
			}
			else {				
				if($this->hasSessionTimeExpire($creationTime)) {
					return false;
				}
				else {
					if(($this->navigatorCheck AND $navigator != $_SERVER["HTTP_USER_AGENT"])
					   OR ($this->langsCheck and $langs AND $langs != A\Util::getIfset($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
					   OR ($this->ipCheck AND $ip != $_SERVER["REMOTE_ADDR"])) {
						
						return false;
					}
					else {
						return true;
					}
				}
			}
		}
		
		/**
		* Generate a new id for the current session
		*/
		public function regenerateId() {
			$this->removeSession($this->sessId);
			
			$this->newSession($this->_vars, true);
		}
		
		/**
		* Destroy current session (reset it and change id)
		*/
		public function destroy() {
			$this->removeSession($this->sessId);
			
			$this->newSession();
		}
		
		############### READ / WRITE METHODS ##########################
		
		protected function saveSession($id, $data) {
			$fileCts = array(
				$data['creationTime'], 
				$data['ip'], 
				$data['navigator'], 
				$data['acceptLanguages'], 
				rawurlencode(serialize($data['data'])), 
			);
//			var_dump(strlen(serialize($data['data'])));
			
			return File::putContents($this->getSessionPath($id), implode("\n", $fileCts), LOCK_EX);
		}
		
		protected function getSessionData($id) {
			$path = $this->getSessionPath($id);
			
			if(is_file($path)) {					
				$sessFileLines = File::getLines($path);
					
				if(count($sessFileLines) > 0) {
					$creationTime = (int)$sessFileLines[0];
					$ip = $sessFileLines[1];
					$navigator = $sessFileLines[2];
					$langs = $sessFileLines[3];
					$vars = unserialize(rawurldecode($sessFileLines[4]));
					
					// return array($creationTime, $ip, $navigator, $langs, $vars);
					return array(
						'creationTime' => $creationTime, 
						'ip' => $ip, 
						'navigator' => $navigator, 
						'acceptLanguages' => $langs, 
						'data' => $vars
					);
				}
				else {
					throw new Exception('Session file is empty (id: '.$id.') '.$_SERVER['REQUEST_URI'].'');
				}
			}
			else {
				throw new Exception('Session file with id: "'.$id.'" does not exist');
			}
		}
		
		/**
		* Remove a session
		*/
		protected function removeSession($id) {
			unlink($this->getSessionPath($id));
		}
		
		protected function getAllSessions() {
			$sessionsFiles = glob($this->storageDir.'/*.session');
			
			return $sessionsFiles;
		}
		
		
		/**
		* Check if it is time to clean session folder (or other storage way)
		*/
		protected function checkClean() {
			if(!is_null($this->cleanInterval)) {
				if(!is_file($this->storageDir.'/'.static::SESSIONS_DATA_FILE_NAME)) {
					File::putContents($this->storageDir.'/'.static::SESSIONS_DATA_FILE_NAME, time());
				}
				else {
					$lastClean = (int)File::getContents($this->storageDir.'/'.static::SESSIONS_DATA_FILE_NAME);
					
					if(($lastClean + $this->cleanInterval) <= time()) {
					
						File::putContents($this->storageDir.'/'.static::SESSIONS_DATA_FILE_NAME, time());
						$this->clean();
					}
				}
			}
		}
		
	}
	
?>